package com.dss.auto_deploy.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dss.auto_deploy.config.DssProperties;
import com.dss.auto_deploy.config.WebsocketEndPoint;
import com.dss.auto_deploy.constants.AutoConstants;
import com.dss.auto_deploy.domain.*;
import com.dss.auto_deploy.dto.*;
import com.dss.auto_deploy.mapper.*;
import com.dss.auto_deploy.service.*;
import com.dss.auto_deploy.util.*;
import com.dss.auto_deploy.vo.AdmRegistryTagHstVo;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.async.ResultCallback;
import com.github.dockerjava.api.model.PushResponseItem;
import com.jcraft.jsch.Session;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.Closeable;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;

/**
* @Description:docker版本号维护Service实现类
* @author dongshoushan
* @date 2022-10-08 11:29:18
*/
@Service
@Slf4j
public class AdmRegistryTagHstServiceImpl  extends ServiceImpl<AdmRegistryTagHstMapper,AdmRegistryTagHst> implements IAdmRegistryTagHstService {

    /**
    *
    * docker版本号维护Mapper
    */
    @Resource
    private  AdmRegistryTagHstMapper admRegistryTagHstMapper;
    @Resource
    private  AdmDockerDeployMapper admDockerDeployMapper;
    @Resource
    private  AdmNamespaceMapper admNamespaceMapper;
    @Resource
    private  AdmDockerRegistryMapper admDockerRegistryMapper;
    @Lazy
    @Resource
    private  IAdmDockerDeployService admDockerDeployService;
    @Resource
    private  ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Resource
    private IAdmNamespaceService admNamespaceService;
    @Resource
    private  DockerUtil dockerUtil;
    @Resource
    private  IAdmDockerService admDockerService;
    @Resource
    private  ServerMapper serverMapper;
    @Resource
    private  IServerService serverService;
    @Resource
    private  RemoteSshUtil remoteSshUtil;
    @Resource
    private  WebsocketEndPoint websocketEndPoint;
    @Resource
    private DssProperties dssProperties;
    /**
    * 新增docker版本号维护
    * @param admRegistryTagHstAddDto
    * @return Long
    */
    @Override
    public int insert(AdmRegistryTagHstAddDto admRegistryTagHstAddDto){
        AdmRegistryTagHst admRegistryTagHst = BeanUtils.copy(admRegistryTagHstAddDto, AdmRegistryTagHst.class);
        admRegistryTagHst.setCreateTime(new Date());
        admRegistryTagHst.setUpdateTime(new Date());
        int insert = admRegistryTagHstMapper.insert(admRegistryTagHst);
        if (insert == 1) {
            log.info("新增成功{}", JacksonUtils.toString(admRegistryTagHst));
        }
        return insert;
    }


    /**
    * 修改docker版本号维护
    * @param admRegistryTagHstUpdateDto
    * @return Long
    */
    @Override
    public int update(AdmRegistryTagHstUpdateDto admRegistryTagHstUpdateDto){
        AdmRegistryTagHst admRegistryTagHst = BeanUtils.copy(admRegistryTagHstUpdateDto, AdmRegistryTagHst.class);
        int number = admRegistryTagHstMapper.updateById(admRegistryTagHst);
        if (number == 1) {
            log.info("修改成功{}",JacksonUtils.toString(admRegistryTagHst));
        }
        return number;
    }


    /**
    * 批量删除docker版本号维护
    * @param ids
    * @return Long
    */
    @Override
    public void deleteBatch(Long[] ids){
        this.removeByIds(Arrays.asList(ids));
    }

    /**
    * docker版本号维护分页查询
    *
    * @param pageDto
    * @return List<AdmRegistryTagHst>
    */
    @Override
    public Page<AdmRegistryTagHstVo> list(RegistryTagHstPageDto pageDto){
        Long registryId = null;
        if (pageDto.getNamespaceId()!=null){
            AdmNamespace admNamespace = SpringUtils.getBean(AdmNamespaceServiceImpl.class).getAll()
                    .stream().filter(s->s.getId().equals(pageDto.getNamespaceId())).findFirst().orElse(null);
            registryId = admNamespace.getRegistryId();
        }
        Page<AdmRegistryTagHst> tablesPage = new Page<>(pageDto.getPage(), pageDto.getLimit());
        LambdaQueryWrapper<AdmRegistryTagHst> wrapper = Wrappers.lambdaQuery(AdmRegistryTagHst.class)
                .eq(pageDto.getProjectId()!=null,AdmRegistryTagHst::getProjectId, pageDto.getProjectId())
                .eq(registryId!=null,AdmRegistryTagHst::getRegistryId, registryId)
                .like(StringUtils.isNotEmpty(pageDto.getContent()),AdmRegistryTagHst::getImageName, pageDto.getContent())
                .like(StringUtils.isNotEmpty(pageDto.getTag()),AdmRegistryTagHst::getTag, pageDto.getTag())
                .orderByDesc(AdmRegistryTagHst::getUpdateTime)
                ;
        Page<AdmRegistryTagHst> pageSelectResult = admRegistryTagHstMapper.selectPage(tablesPage, wrapper);
        List<AdmRegistryTagHst> records = pageSelectResult.getRecords();
        List<AdmRegistryTagHstVo> voList = convertToVo(records);
        Page<AdmRegistryTagHstVo> page = new Page<>();
        page.setRecords(voList);
        page.setTotal(pageSelectResult.getTotal());
        page.setSize(pageSelectResult.getSize());
        return page;
    }

    private List<AdmRegistryTagHstVo> convertToVo(List<AdmRegistryTagHst> records) {
        if (records == null || records.isEmpty()){
            return new ArrayList<>();
        }
        List<AdmRegistryTagHstVo> voList = BeanUtils.copyList(records, AdmRegistryTagHstVo.class);
        List<AdmNamespace> namespaceList = SpringUtils.getBean(AdmNamespaceServiceImpl.class).getAll();
        List<AdmProject> admProjects = SpringUtils.getBean(AdmProjectServiceImpl.class).getAll();
        List<AdmDockerRegistry> admDockerRegistries = SpringUtils.getBean(AdmDockerRegistryServiceImpl.class).getAll();
        voList.forEach(vo -> {
            AdmProject admProject = admProjects.stream().filter(s -> s.getId().equals(vo.getProjectId())).findFirst().orElse(null);
            vo.setProjectName(admProject.getProjectName());
            AdmDockerRegistry admDockerRegistry = admDockerRegistries.stream().filter(s -> s.getId().equals(vo.getRegistryId())).findFirst().orElse(null);
            vo.setRegistryName(admDockerRegistry.getRegistryName());
            AdmNamespace admNamespace = namespaceList.stream().filter(s -> s.getId().equals(vo.getNamespaceId())).findFirst().orElse(null);
            vo.setNamespaceName(admNamespace.getName());
        });
        return voList;
    }

    /**
     * 导出镜像
     */
    @Override
    public List<SysFileDto> exportImages(List<String> idList) throws Exception {
        List<SysFileDto> pathList = new ArrayList<>();
        List<AdmRegistryTagHst> registryTagHstList = admRegistryTagHstMapper.selectBatchIds(idList);
        AdmRegistryTagHst admRegistryTagHst = registryTagHstList.get(0);
        AdmDockerRegistry admDockerRegistry = admDockerRegistryMapper.selectById(admRegistryTagHst.getRegistryId());
        Long serverId = admDockerRegistry.getServerId();
        Server registryServer = serverMapper.selectById(serverId);
        //获取仓库的所在服务器的docker对象
        AdmDocker admDocker = admDockerService.getByRegistryId(admRegistryTagHst.getRegistryId());
        Session session = remoteSshUtil.connect(registryServer);
        //先在仓库所在机器进行pull拉取镜像
        DockerClient dockerClient = dockerUtil.getDockerClientByDocker(SpringUtils.getBean(AdmDockerServiceImpl.class).convertToVo(admDocker));
        CountDownLatch mainLatch = new CountDownLatch(registryTagHstList.size());
        registryTagHstList.stream().<Runnable>map(registryTagHst -> () -> {
            String imageFullName = StringUtils.format("{}:{}/{}:{}", registryServer.getIp(), admDockerRegistry.getPort(), registryTagHst.getImageName(), registryTagHst.getTag());
            //拉取并保存
            try {
                remoteSshUtil.createDir(AutoConstants.LINUX_TEMP_PATH,registryServer);
                dockerUtil.pullAndSave(dockerClient, imageFullName, registryTagHst, AutoConstants.DOCKER_DOWNLOAD_TEMP_PATH, session, mainLatch);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            String fileName = StringUtils.format("{}.{}.tar", registryTagHst.getImageName(), registryTagHst.getTag());
            pathList.add(SysFileDto.builder().path(AutoConstants.DOCKER_DOWNLOAD_TEMP_PATH+"/"+fileName).name(fileName).build());
        }).forEach(threadPoolTaskExecutor::execute);
        mainLatch.await();
        dockerUtil.close(dockerClient);
        remoteSshUtil.close(session);
        //下载
        return pathList;

    }

    /**
     * 导入jar包镜像
     * @param file
     */
    @Override
    public void importJarImages(Long projectId, Long namespaceId,MultipartFile file) {
        AdmNamespace admNamespace = admNamespaceMapper.selectById(namespaceId);
        Long registryId = admNamespace.getRegistryId();
        Server server = serverService.getServerByRegistryId(registryId);
        AdmDockerRegistry admDockerRegistry = admDockerRegistryMapper.selectById(registryId);
        System.out.println(StringUtils.format("上传到仓库地址IP：{} 目录：{}",server.getIp(), AutoConstants.LINUX_TEMP_PATH));
        remoteSshUtil.createDir(AutoConstants.LINUX_TEMP_PATH,server);
        AdmDocker registryDocker = admDockerService.getByRegistryId(registryId);
        DockerClient registryDockerClient = dockerUtil.getDockerClientByDocker(SpringUtils.getBean(AdmDockerServiceImpl.class).convertToVo(registryDocker));
        Session session =null;
        try {
            session = remoteSshUtil.connect(server);
            remoteSshUtil.putFile(session,file,AutoConstants.LINUX_TEMP_PATH,file.getSize(),null,"importJarImages",server.getIp());
            String originalFilename = file.getOriginalFilename();
            //先删除仓库
            String replace = originalFilename.replace(".tar", "");
            String imageName = replace.substring(0, replace.indexOf("."));
            String tag = replace.substring(replace.indexOf(".")+1);
            admDockerDeployService.delRegistry(server,admDockerRegistry,imageName,tag);
            dockerUtil.delContainer(registryDockerClient,imageName);
            dockerUtil.delImageByName(registryDockerClient,imageName);

            sendMsg("loading",file.getOriginalFilename());
            String s = StringUtils.format(AutoConstants.LOAD_IMAGES, AutoConstants.LINUX_TEMP_PATH, file.getOriginalFilename());
            String exec = remoteSshUtil.exec(s, session);
            System.out.println(s);
            System.out.println(exec);
            //保存仓库版本记录
            saveTagHst(exec,projectId,registryId,session,namespaceId);
            Thread.sleep(5000L);
            remoteSshUtil.exec(StringUtils.format("rm -rf {}/{}",AutoConstants.LINUX_TEMP_PATH,file.getOriginalFilename()), session);
            sendMsg("complete",file.getOriginalFilename());
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new ServiceException(e.getMessage());
        }finally {
            remoteSshUtil.close(session);
            dockerUtil.close(registryDockerClient);
        }
    }

    private void sendMsg(String status,String name){
        websocketEndPoint.send(WsSendDto.builder().type(0).table("importJarImages").status(status).name(name).build());
    }
    /**
     * 查询镜像版本
     * @param dto
     * @return
     */
    @Override
    public List<AdmRegistryTagHst> getTagHst(DockerTagHstQueryDto dto) {
        QueryWrapper<AdmRegistryTagHst> queryWrapper = Wrappers.query();
        AdmNamespace admNamespace = admNamespaceMapper.selectById(dto.getNamespaceId());
        queryWrapper.eq("project_id",dto.getProjectId()).eq("registry_id",admNamespace.getRegistryId())
                .eq("image_name",dto.getModuleName()).orderByDesc("create_time");
        List<AdmRegistryTagHst> list = admRegistryTagHstMapper.selectList(queryWrapper);
        return list;
    }

    @Override
    public void deployProduction(List<String> idList) throws Exception {
        //导出到本地
        exportImages(idList);
        List<AdmRegistryTagHst> tagHstList = admRegistryTagHstMapper.selectBatchIds(idList);
        CountDownLatch mainLatch = new CountDownLatch(idList.size());
        tagHstList.forEach(admRegistryTagHst -> threadPoolTaskExecutor.execute(() -> {
            Long projectId = admRegistryTagHst.getProjectId();
            String tag = admRegistryTagHst.getTag();
            List<AdmDockerDeploy> admDockerDeploys = admDockerDeployMapper.selectList(Wrappers.lambdaQuery(AdmDockerDeploy.class)
                    .eq(AdmDockerDeploy::getProjectId, admRegistryTagHst.getProjectId()));
            List<Long> spaceId = admDockerDeploys.stream().map(AdmDockerDeploy::getNamespaceId).collect(Collectors.toList());
            List<AdmNamespace> admNamespaces = admNamespaceMapper.selectBatchIds(spaceId);
            AdmNamespace admNamespace = admNamespaces.stream().filter(s -> s.getName().contains(dssProperties.getProdNameSpaceKey())).findFirst().orElse(null);
            if (admNamespace == null) {
                throw new ServiceException("请先配置此项目的生产模块。其中命名空间必须包含关键字：" + dssProperties.getProdNameSpaceKey());
            }
            String fileName = StringUtils.format("{}.{}.tar", admRegistryTagHst.getImageName(), tag);
            //上传镜像
            importJarImages(projectId, admNamespace.getId(), FileUtils.getMultipartFile(AutoConstants.DOCKER_DOWNLOAD_TEMP_PATH + "/" + fileName));
            mainLatch.countDown();
            File file = new File(AutoConstants.DOCKER_DOWNLOAD_TEMP_PATH + "/" + fileName);
            file.delete();
        }));
        mainLatch.await();

    }

    private void saveTagHst(String exec, Long projectId, Long registryId, Session session,Long namespaceId) throws Exception {
        Server server = serverService.getServerByRegistryId(registryId);
        String imageFullName = exec.replace("Loaded image: ","").trim();
        AdmDocker admDocker = admDockerService.getByRegistryId(registryId);
        AdmDockerRegistry admDockerRegistry = admDockerRegistryMapper.selectById(registryId);
        String[] split = imageFullName.split("/");
        String[] strings = split[1].split(":");
        String imageName = strings[0];
        String tag = strings[1];

        String newImageName = StringUtils.format("{}:{}/{}:{}",server.getIp(),admDockerRegistry.getPort(),imageName,tag);
        remoteSshUtil.exec(StringUtils.format("docker tag {} {}",imageFullName,newImageName),session);
        System.out.println(StringUtils.format("docker tag {} {}",imageFullName,newImageName));
        Thread.sleep(200);
        DockerClient dockerClient = dockerUtil.getDockerClientByDocker(SpringUtils.getBean(AdmDockerServiceImpl.class).convertToVo(admDocker));
        dockerClient.pushImageCmd(newImageName).exec(new ResultCallback<PushResponseItem>() {

            @Override
            public void close() {

            }

            @Override
            public void onStart(Closeable closeable) {
                System.out.println("开始推送..."+newImageName);
            }

            @Override
            public void onNext(PushResponseItem object) {
                //if (StringUtils.isNotEmpty(object.getProgress())) {
                //    System.out.println(object.getStatus() + "" + object.getProgress());
                //}
            }

            @Override
            public void onError(Throwable throwable) {

            }

            @Override
            public void onComplete() {
                //仓库历史记录
                List<AdmRegistryTagHst> registryTagHstList = admRegistryTagHstMapper.selectList(Wrappers.lambdaQuery(AdmRegistryTagHst.class)
                        .eq(AdmRegistryTagHst::getRegistryId,registryId)
                        .eq(AdmRegistryTagHst::getNamespaceId,namespaceId)
                        .eq(AdmRegistryTagHst::getImageName,imageName)
                );
                List<AdmRegistryTagHst> collect2 = registryTagHstList.stream().filter(admRegistryTagHst -> tag.equals(admRegistryTagHst.getTag())).collect(Collectors.toList());
                if (CollectionUtil.isEmpty(collect2)) {
                    AdmRegistryTagHst registryTagHst = AdmRegistryTagHst.builder().projectId(projectId).registryId(registryId).namespaceId(namespaceId).imageName(imageName).tag(tag).createTime(new Date()).updateTime(new Date()).build();
                    admRegistryTagHstMapper.insert(registryTagHst);
                }else {
                    for (AdmRegistryTagHst admRegistryTagHst : collect2) {
                        admRegistryTagHst.setUpdateTime(new Date());
                        admRegistryTagHstMapper.updateById(admRegistryTagHst);
                    }
                }
                admDockerDeployService.delHstTag(imageName,registryId);
                dockerUtil.close(dockerClient);
                System.out.println("推送完成..."+newImageName);

                try {
                    AdmNamespace namespace = admNamespaceService.getById(namespaceId);
                    admDockerDeployService.changeTag(ChangeTagDto.builder().namespaceId(namespace.getId()).tag(tag).projectId(projectId).moduleName(imageName).build());
                } catch (Exception e) {
                    throw new ServiceException(e.getMessage());
                }
            }
        });

    }

}
